#include "glwidget.h"

#include "gltexture.h"

#include <QGLShaderProgram>
#include <QMatrix4x4>
#include <QVector>
#include <QVector2D>
#include <QVector4D>

GLWidget::GLWidget(QWidget *parent)
    : QGLWidget(parent)
{
}

GLWidget::~GLWidget()
{
}

GLubyte* GLWidget::genCheckImage(unsigned width, unsigned height, GLsizei checkSize)
{
    unsigned    x,
                y;
    GLubyte *pixels = new GLubyte[ width * height * 3];

    if ( pixels == NULL )
       return NULL;

    for ( y = 0; y < height; y++ )
       for ( x = 0; x < width; x++ )
       {
          GLubyte rColor = 0;
          GLubyte bColor = 0;

          if ( ( x / checkSize ) % 2 == 0 )
          {
             rColor = 255 * ( ( y / checkSize ) % 2 );
             bColor = 255 * ( 1 - ( ( y / checkSize ) % 2 ) );
          }
          else
          {
             bColor = 255 * ( ( y / checkSize ) % 2 );
             rColor = 255 * ( 1 - ( ( y / checkSize ) % 2 ) );
          }

          pixels[(y * height + x) * 3] = rColor;
          pixels[(y * height + x) * 3 + 1] = 0;
          pixels[(y * height + x) * 3 + 2] = bColor;
       }

    return pixels;
}

void GLWidget::createTexture2D()
{
    int    width = 256,
           height = 256;
    GLubyte *pixels;

    pixels = genCheckImage( width, height, 64 );

    GLTexture *newTexture = new GLTexture(pixels, width, height, GLTexture::FormatRGB, GLTexture::Target2D);
    delete [] pixels;

    newTexture->setMinificationFilter(GLTexture::MinificationLinear);
    newTexture->setMagnificationFilter();

    _userObject.setTexture(QSharedPointer<GLTexture>(newTexture));
}

void GLWidget::initializeGL()
{
    const char *vShaderStr =
            "uniform float u_offset;      \n"
            "attribute vec4 a_position;   \n"
            "attribute vec2 a_texCoord;   \n"
            "varying vec2 v_texCoord;     \n"
            "void main()                  \n"
            "{                            \n"
            "   gl_Position = a_position; \n"
            "   gl_Position.x += u_offset;\n"
            "   v_texCoord = a_texCoord;  \n"
            "}                            \n";
    const char *fShaderStr =
// throws a shader compile error            "precision mediump float;                            \n"
            "mediump float;                            \n"
            "varying vec2 v_texCoord;                            \n"
            "uniform sampler2D s_texture;                        \n"
            "void main()                                         \n"
            "{                                                   \n"
            "  gl_FragColor = texture2D( s_texture, v_texCoord );\n"
            "}                                                   \n";

    _userObject.loadProgram(vShaderStr, fShaderStr);
    // Get the attribute locations
    _userObject.setPositionLocation(_userObject.programObject()->attributeLocation("a_position"));
    _userObject.setTexCoordLocation(_userObject.programObject()->attributeLocation("a_texCoord"));

    // Get the sampler location
    _userObject.setSamplerLocation(_userObject.programObject()->uniformLocation("s_texture"));

    // Get the mvp location
    _userObject.setMvpLocation(_userObject.programObject()->uniformLocation("u_mvp"));

    //Get the offset location
    _userObject.setOffsetLocation(_userObject.programObject()->uniformLocation("u_offset"));

    //Load the texture
    createTexture2D();

    glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
}

void GLWidget::paintGL()
{
    QVector<QVector4D> vertexCoords;
    vertexCoords.reserve(4);
    vertexCoords
            <<  QVector4D(-0.3f, 0.3f, 0.0f, 1.0f)
            <<  QVector4D(-0.3f, -0.3f, 0.0f, 1.0f)
            <<  QVector4D(0.3f, -0.3f, 0.0f, 1.0f)
            <<  QVector4D(0.3f, 0.3f, 0.0f, 1.0f);
    QVector<QVector2D> textureCoords;
    textureCoords.reserve(4);
    textureCoords
            <<  QVector2D(-1.0f, -1.0f)
            <<  QVector2D(-1.0f, 2.0f)
            <<  QVector2D(2.0f, 2.0f)
            <<  QVector2D(2.0f, -1.0f);
    GLushort indices[] = { 0, 1, 2, 0, 2, 3 };

    // Set the viewport
    glViewport(0, 0, width(), height());

    // Clear the color buffer
    glClear(GL_COLOR_BUFFER_BIT);

    // Use the program object
    _userObject.programObject()->bind();

    // Load the vertex position
    _userObject.programObject()->setAttributeArray(_userObject.positionLocation(), vertexCoords.constData());

    // Load the texture coords position
    _userObject.programObject()->setAttributeArray(_userObject.texCoordLocation(), textureCoords.constData());

    _userObject.programObject()->enableAttributeArray(_userObject.positionLocation());
    _userObject.programObject()->enableAttributeArray(_userObject.texCoordLocation());

    // Bind the texture
    _userObject.texture()->activate(0);
    _userObject.texture()->bind();

    // Set the sampler texture unit to 0
    _userObject.programObject()->setUniformValue(_userObject.samplerLocation(), 0);

    // Draw quad with repeat wrap mode
    _userObject.texture()->setSCoordinateWrap();
    _userObject.texture()->setTCoordinateWrap();
    _userObject.programObject()->setUniformValue(_userObject.offsetLocation(), -0.7f);
    glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_SHORT, indices);

    // Draw quad with clamp to edge wrap mode
    _userObject.texture()->setSCoordinateWrap(GLTexture::WrapClampToEdge);
    _userObject.texture()->setTCoordinateWrap(GLTexture::WrapClampToEdge);
    _userObject.programObject()->setUniformValue(_userObject.offsetLocation(), 0.0f);
    glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_SHORT, indices);

    // Draw quad with mirrored repeat
    _userObject.texture()->setSCoordinateWrap(GLTexture::WrapMirroredRepeat);
    _userObject.texture()->setTCoordinateWrap(GLTexture::WrapMirroredRepeat);
    _userObject.programObject()->setUniformValue(_userObject.offsetLocation(), 0.7f);
    glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_SHORT, indices);
}

void GLWidget::resizeGL(int width, int height)
{
    // nothing to do
    (void)width;
    (void)height;
}
